﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using KBEngine.Export;
using KBEngine.Setting.TypeDef;

namespace KBEngineTool.Export
{
    class CSharpCodeExport : CSharpExport
    {
        public static readonly string FileName = "UserTypes.cs";
        private List<BaseType> m_types;
        int _variablesIndex;

        public CSharpCodeExport(List<BaseType> types)
        {
            m_types = types;
        }
        internal bool Save(string dir)
        {
            _variablesIndex = 1;
            var path = Path.Combine(dir, FileName);

            CodeWriter codeWriter = new CodeWriter(fileMark: "// DO NOT modify this file directly. It was automatically generated by UserTypeDefTools.");
            codeWriter.Writeln("using System;");
            codeWriter.Writeln("using System.Collections.Generic;");
            codeWriter.Writeln("using System.Linq;");
            codeWriter.Writeln("using System.Text;");
            codeWriter.Writeln();
            codeWriter.Writeln("namespace KBEngine");
            codeWriter.StartBlock();
            //type
            codeWriter.Writeln("#region UserTypes");
            codeWriter.Writeln();
            foreach (var item in m_types)
            {
                var userType = item as UserType;
                if (userType == null)
                    continue;

                codeWriter.Writeln("#region {0}", userType.TypeName);
                codeWriter.Writeln();
                codeWriter.Writeln($"public class {userType.TypeName}");
                codeWriter.StartBlock();
                foreach (var proper in userType.Properties)
                {
                    string typeStr = GetTypeName(proper.Type);
                    codeWriter.Writeln($"public {typeStr} {proper.FieldName};");
                }
                codeWriter.EndBlock();
                codeWriter.Writeln();
                codeWriter.Writeln("#endregion");
                codeWriter.Writeln();
            }
            codeWriter.Writeln("#endregion");
            codeWriter.Writeln();

            //helper
            codeWriter.Writeln("#region Helper");
            codeWriter.Writeln();
            codeWriter.Writeln("public static class TypeCastHelper");
            codeWriter.StartBlock();
            foreach (var item in m_types)
            {
                var userType = item as UserType;
                if (userType != null)
                    WriteTypeHelper(codeWriter, userType);
            }
            codeWriter.EndBlock();
            codeWriter.Writeln();
            codeWriter.Writeln("#endregion");

            //end
            codeWriter.EndBlock();
            codeWriter.Save(path);
            return true;
        }

        private void WriteTypeHelper(CodeWriter codeWriter, UserType userType)
        {
            var typeName = userType.TypeName;
            codeWriter.Writeln("public static {0} Get_{0}(this Dictionary<string, object> dict)", typeName);
            codeWriter.StartBlock();
            codeWriter.Writeln("var ret = new {0}();", typeName);
            foreach (var field in userType.Properties)
            {
                if (field.Type is UserType)
                {
                    codeWriter.Writeln("ret.{0} = ((Dictionary<string, object>)dict[\"data\"]).Get_{1}();", field.FieldName, GetTypeName(field.Type));
                }
                else if (field.Type is ArrayType)
                {
                    var arrayType = (ArrayType)field.Type;
                    codeWriter.Writeln("ret.{0} = (from av in (List<object>)dict[\"{0}\"] select ( {1} ) ).ToList();", field.FieldName, GetArrayTypeHelperFunCode("av", arrayType));
                }
                else
                {
                    codeWriter.Writeln("ret.{0} = ({1})dict[\"{0}\"];", field.FieldName, GetTypeName(field.Type));
                }
            }

            codeWriter.Writeln("return ret;");
            codeWriter.EndBlock();
            codeWriter.Writeln();
        }
        string GetArrayTypeHelperFunCode(string collection, ArrayType type)
        {
            StringBuilder sb = new StringBuilder();

            if (type.ElementType is UserType)
            {
                sb.AppendFormat("( (Dictionary<string, object>){0} ).Get_{1}()", collection, GetTypeName(type.ElementType));
            }
            else if (type.ElementType is ArrayType)
            {
                sb.AppendFormat("( from tempE_{0} in (List<object>){1} select ( {2} ) ).ToList()", ++_variablesIndex, collection, GetArrayTypeHelperFunCode("tempE_" + _variablesIndex.ToString(), type.ElementType as ArrayType));
            }
            else
                sb.AppendFormat("({0}){1}", GetTypeName(type.ElementType), collection);
            return sb.ToString();
        }
    }
}
